package online.inote.mapper.core;

import org.apache.commons.lang3.StringUtils;
import org.springframework.asm.AnnotationVisitor;
import org.springframework.asm.ClassWriter;
import org.springframework.asm.MethodVisitor;
import org.springframework.asm.Opcodes;

import lombok.NoArgsConstructor;
import online.inote.mapper.annotation.GenerateMapper;
import online.inote.mapper.utils.ClassUtils;

@NoArgsConstructor(access = lombok.AccessLevel.PRIVATE)
public class ServiceGenerator extends Generator implements Opcodes {

	static ServiceGenerator getInstance() {
		return new ServiceGenerator();
    }
	
    Class<?> build(Class<?> signature, Class<?> superClass) {
		ClassWriter cw = new ClassWriter(0);
		String className = getClassName(signature);
		
		logger.info("Extend-mapper: Generated class:[ {} ], signature:[ {} ]", className, signature.getName());
		
		cw.visit(V1_8, ACC_PUBLIC + ACC_SUPER, getPath(className), getSignature(signature, superClass),
				ClassUtils.getClassPath(superClass), null);

		cw.visitAnnotation("Lorg/springframework/stereotype/Service;", true).visitEnd();
		transactionManagerHandle(signature, cw);
		
		MethodVisitor mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
		mv.visitCode();
		mv.visitVarInsn(ALOAD, 0);
		mv.visitMethodInsn(INVOKESPECIAL, "online/inote/mapper/base/BaseService", "<init>", "()V", false);
		mv.visitInsn(RETURN);
		mv.visitMaxs(1, 1);
		mv.visitEnd();
		
		cw.visitEnd();

		byte[] code = cw.toByteArray();
		
		return defineClass(className, code, 0, code.length);
    }
	
	private void transactionManagerHandle(Class<?> generic, ClassWriter cw) {
		GenerateMapper generateMapper = generic.getAnnotation(GenerateMapper.class);
		
		if (generateMapper == null) {
			throw new RuntimeException("获取注解信息失败");
		}
		
		if (StringUtils.isNotBlank(generateMapper.transactionManager())) {
			AnnotationVisitor av = cw.visitAnnotation("Lorg/springframework/transaction/annotation/Transactional;", true);
			av.visit("value", generateMapper.transactionManager());
			av.visitEnd();
		}
	}

	@Override
	public Type getType() {
		return Type.SERVICE;
	}
}